import mountElement from './mountElement.js'
import diffComponent from './diffComponent.js'
import createDOMElement from './createDOMElement'
import updateTextNode from './updateTextNode'
import updateElementNode from './updateElementNode'
import {unmount} from "./utils"
export default function diff (virtualDOM, container, oldDOM) {
    console.log('virtualDOM...', virtualDOM, container, oldDOM);
    // 获取未更新前的 Virtual DOM
    const oldVirtualDOM = oldDOM && oldDOM._virtualDOM
    // 获取未更新前的 组件
    const oldComponent = oldVirtualDOM && oldVirtualDOM.component
    // 判断 oldDOM(老的真实节点) 是否存在
    if (!oldDOM) {
        // 如果不存在 不需要对比 直接将 Virtual DOM 转换为真实 DOM
        mountElement(virtualDOM, container)
    } else if (
        // 如果要对比的两个节点类型不同
        virtualDOM.type !== oldVirtualDOM.type &&
        // 并且节点的类型不是组件，因为组件要单独维护
        typeof virtualDOM.type !== 'function'
    ) {
        // 生成新的 DOM 对象替换老的 DOM 对象
        const newElement = createDOMElement(virtualDOM)
        oldDOM.parentNode.replaceChild(newElement, oldDOM)
    } else if (typeof virtualDOM.type === 'function') {
        // 要更新的是组件
        // 1) 组件本身的 virtualDOM 对象 通过它可以获取到组件最新的 props
        // 2) 要更新的组件的实例对象 通过它可以调用组件的生命周期函数 可以更新组件的 props 属性 可以获取到组件返回的最新的 Virtual DOM
        // 3) 要更新的 DOM 象 在更新组件时 需要在已有DOM对象的身上进行修改 实现DOM最小化操作 获取旧的 Virtual DOM 对象
        // 4) 如果要更新的组件和旧组件不是同一个组件 要直接将组件返回的 Virtual DOM 显示在页面中 此时需要 container 做为父级容器
        diffComponent(virtualDOM, oldComponent, oldDOM, container)
    } else if (oldVirtualDOM && virtualDOM.type === oldVirtualDOM.type) {
        if (virtualDOM.type === "text") {
            // 文本节点 对比文本内容是否发生变化
            updateTextNode(virtualDOM, oldVirtualDOM, oldDOM)
        } else {
            // 元素节点 对比元素属性是否发生变化
            updateElementNode(oldDOM, virtualDOM, oldVirtualDOM)
        }
        // 1. 将拥有key属性的子元素放置在一个单独的对象中
        let keyedElements = {}
        for (let i = 0, len = oldDOM.childNodes.length; i < len; i++) {
            let domElement = oldDOM.childNodes[i]
            if (domElement.nodeType === 1) {
                let key = domElement.getAttribute("key")
                if (key) {
                    keyedElements[key] = domElement
                }
            }
        }

        let hasNoKey = Object.keys(keyedElements).length === 0

        if (hasNoKey) {
            // 对比子节点
            virtualDOM.children.forEach((child, i) => {
                diff(child, oldDOM, oldDOM.childNodes[i])
            })
        } else {
            // 2. 循环 virtualDOM 的子元素 获取子元素的 key 属性
            virtualDOM.children.forEach((child, i) => {
                let key = child.props.key
                if (key) {
                    let domElement = keyedElements[key]                    
                    if (domElement) {
                        // 3. 看看当前位置的元素是不是我们期望的元素
                        if (oldDOM.childNodes[i] && oldDOM.childNodes[i] !== domElement) {
                            oldDOM.insertBefore(domElement, oldDOM.childNodes[i])
                        }
                    } else {
                        // 新增元素
                        const childElement = createDOMElement(child)
                        oldDOM.insertBefore(childElement, oldDOM.childNodes[i])
                    }
                }
            })
        }
        // 删除节点
        // 获取旧节点
        let oldChildNodes = oldDOM.childNodes
        // 判断旧节点的数量
        if (oldChildNodes.length > virtualDOM.children.length) {
            if (hasNoKey) {
                // 有节点需要被删除
                for (
                    let i = oldChildNodes.length - 1;
                    i > virtualDOM.children.length - 1;
                    i--
                ) {
                    unmount(oldChildNodes[i])
                }
            } else {
                // 通过key属性删除节点
                for (let i = 0; i < oldChildNodes.length; i++) {
                    let oldChild = oldChildNodes[i]
                    let oldChildKey = oldChild._virtualDOM.props.key
                    let found = false
                    for (let n = 0; n < virtualDOM.children.length; n++) {
                        if (oldChildKey === virtualDOM.children[n].props.key) {
                            found = true
                            break
                        }
                    }
                    if (!found) {
                        unmount(oldChild)
                    }
                }
            }
        }
    }
}